/**************************
 * Includes
 *
 **************************/

#include <stdio.h>
#include <string>
#include <fstream>
#include <vector>
#include <thread>
#include <ctime>

#include <iostream>
//#include <fcntl.h>


#include <GL/glew.h>
//#include <GL/wglew.h>
#include <GL/gl.h>


#include "../BSP.h"
//#include "Camera.h"
#include "../GLRenderer.h"
//#include "Thread.h"

#include "../signalslot.h"

#include "globals.h"

#include "EditorWindow.h"
#include "TextureWindow.h"
#include "ChooseTextureWindow.h"
#include "Editor.h"
#include "../Entities.h"
#include "../leak.h"

#undef main


using namespace std;


#define MAX_BRUSH_SIDES 10240

const float UPDATE_DELTA = 1000.0f/60.0f;

/**************************
 * Function Declarations
 *
 **************************/


void Function();


/*cSignal< int, float, float > click_signal;
cSignal< int, float, float > move_signal;
cSignal< int, float, float > release_signal;
cSignal< int, vector<unsigned char>& > key_press_signal;
cSignal< int, float, float > right_click_signal;
cSignal< int, bool > wheel_signal;*/
SignalSet signals;



int g_num_brushes_done = 0;


bool view_update_needed = true;


using namespace std;

void RunCommand(string);

void RunNewCommand(std::string line);

int main()
{
	if(SDL_Init(SDL_INIT_VIDEO) < 0)
	{
		//sdldie("Unable to initialize SDL");
	}

	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);

	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32);
	SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);

	g_num_threads = thread::hardware_concurrency();
	cout << "num_threads: " << g_num_threads << endl;

	g_prev_time = SDL_GetTicks();
	

	g_builder_brush = new BuilderBrush();
	g_selection_frame.reset(new SelectionFrame());

	EditorWindow main_window("MapEditor", 1024, 768, NORMAL_WINDOW);

	//SDL_SetWindowPosition(main_window.window(), 800, 800);

	Renderer::Init(&g_render_state);


	RunCommand("LoadFile map_src/test17.txt");

	g_tex_window.reset(new TextureWindow());


	main_window.Show();
	//g_tex_window->Show();

	g_choose_tex_window.reset(new ChooseTextureWindow());
	//g_choose_tex_window->Show();

	//Renderer::SetVSync(true);
	SDL_GL_SetSwapInterval(1);
		
	g_renderer = new Renderer(800, 800);
	g_renderer->set_DrawFunc(Function);

		
	float cam_pos[3] = {10.0, 0.0, 0.0};
	float cam_target[3] = {0.0, 0.0, 0.0};
	float up[3] = {0.0, 1.0, 0.0};
	Camera cam(cam_pos, cam_target, up);
	cam.Perspective(45, 1.0f, 0.1f, 10000.0f);

	g_render_state.current_cam = &cam;

			
	for(int i = 0; i < 1024; i++)
	{
		color_table[i][0] = ((float)(rand() % 256))/(256.0f);
		color_table[i][1] = ((float)(rand() % 256))/(256.0f);
		color_table[i][2] = ((float)(rand() % 256))/(256.0f);
	}


	g_checker_texture = Renderer::LoadTGATexture("textures/checker.tga");

	/*signals.click_signal.Connect(main_window.click);
	move_signal.Connect(main_window.move);
	release_signal.Connect(main_window.release);
	key_press_signal.Connect(main_window.key_press); */
	main_window.Connect(signals);
	//signals.right_click_signal.Connect(main_window.right_click);
	//signals.wheel_signal.Connect(main_window.wheel_slot);


	/*click_signal.Connect(g_tex_window->click);
	move_signal.Connect(g_tex_window->move);
	release_signal.Connect(g_tex_window->release);
	key_press_signal.Connect(g_tex_window->key_press);*/
	g_tex_window->Connect(signals);

	/*click_signal.Connect(g_choose_tex_window->click);
	move_signal.Connect(g_choose_tex_window->move);
	release_signal.Connect(g_choose_tex_window->release); */
	g_choose_tex_window->Connect(signals);
	


	//g_entities.push_back(std::unique_ptr<BaseEntity>(new SkyCamera()));
	//g_entities.emplace_back(new SkyCamera());

	//RunCommand("AddMesh model.mesh");
	//RunCommand("AddMesh model.mesh");

	//g_static_meshes[0].RotateY(0.8f);
	//g_static_meshes[0].Move(0.0f, 10.0f, 0.0f);

	//RunCommand("ImportFile map_src/test18.txt");
	//RunCommand("LoadFile map_src/test18.txt");
	//RunNewCommand("CreateBoxBrushBB 17.0");
	//RunNewCommand("AddMesh model.mesh");
	//RunCommand("MoveBuilderBrush 0.0 16.0 0.0");
	//RunCommand("Undo");
	//RunCommand("Redo");

    /* program main loop */
    while (!g_quit)
    {
		/*g_focused_window = GetFocus();
		if(g_focused_window != main_window.window_handle() && g_focused_window != g_tex_window->window_handle() && g_focused_window != g_choose_tex_window->window_handle())
		{
			Sleep(500);
			continue;
		}*/

    	FireSignals(signals.click_signal, signals.release_signal, signals.move_signal, signals.key_press_signal, &signals.right_click_signal, &signals.wheel_signal, true);

		g_cur_time = SDL_GetTicks();
		g_delta_time = g_cur_time - g_prev_time;
		if(g_delta_time > 200)
		{
			g_delta_time = 200;
		}
		g_prev_time = g_cur_time;
		g_time_acc += g_delta_time;

		while(g_time_acc >= UPDATE_DELTA)
		{
			main_window.UpdateActiveCam(UPDATE_DELTA);

			g_time_acc -= UPDATE_DELTA;
		}


		//cam.UpdatePlanes();

		main_window.UpdateActiveCamPlanes();

		main_window.MakeCurrent();

		//if(view_update_needed)
		//{

			main_window.SetupViewPort(RIGHT_TOP);

			glClearColor(0.0, 0.0, 0.0, 1.0);

			g_renderer->Draw();

			glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

			/**/

			glLineWidth(3.0f);

			main_window.SetupAuxViewPort(LEFT_TOP);
			glClearColor(0.0, 0.0, 0.0, 1.0);
			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			g_renderer->Draw();
			main_window.DrawGrid(LEFT_TOP);
			glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

			main_window.SetupAuxViewPort(LEFT_BOTTOM);
			glClearColor(0.0, 0.0, 0.0, 1.0);
			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			g_renderer->Draw();
			main_window.DrawGrid(LEFT_BOTTOM);
			glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

			main_window.SetupAuxViewPort(RIGHT_BOTTOM);
			glClearColor(0.0, 0.0, 0.0, 1.0);
			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			g_renderer->Draw();
			main_window.DrawGrid(RIGHT_BOTTOM);
			glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

		//}


		glClearColor(0.0, 0.0, 0.0, 1.0);

		glDisable(GL_DEPTH_TEST);

		Renderer::SwitchProgram(SCREEN_DRAW_PROGRAM);

		main_window.Draw();


		main_window.Apply();


		if(g_tex_window->focused())
		{
			g_tex_window->MakeCurrent();
			g_tex_window->Draw();
			g_tex_window->Apply();
		}

		if(g_choose_tex_window->focused())
		{
			g_choose_tex_window->MakeCurrent();
			g_choose_tex_window->Draw();
			g_choose_tex_window->Apply();
		}
	}

	delete g_builder_brush;
	delete g_renderer;
	g_selection_frame.reset();

	//delete[] g_brushes;
	g_brushes.clear();

	//StopConsoleInput();
	SDL_Quit();

	PRINT_UNFREED
	return 0;
}



void Function() {

	lock_guard<mutex> render_lock(g_render_state.render_mtx);

	Renderer::SwitchProgram(MAIN_PROGRAM);

	float view_proj_mat[16];
	matrixMultiplyMM(view_proj_mat, (float*)g_render_state.current_cam->projection_matrix(), (float*)g_render_state.current_cam->view_matrix());

	glUniformMatrix4fv(g_render_state.current_program->uniform(MVP_UNIFORM), 1, GL_FALSE, view_proj_mat);

	//cout << g_num_brushes << endl;

	/*for(int i = 0; i < g_num_brushes; i++)
	{
		g_brushes[i].Draw(i);
	}*/

	//Renderer::SwitchProgram(CONSTANT_PROGRAM);

	glUniform3f(Renderer::render_state->current_program->uniform(COL_UNIFORM), 0.0f, 0.9f, 0.9f);

	Renderer::SwitchTexture(DIFFUSEMAP_SLOT, g_checker_texture);
	Renderer::CheckGLError("BeforeDraw");
	int i = 0;
	for(auto brush = g_brushes.begin(); brush != g_brushes.end(); brush++)
	{
		//(*brush).Draw(i);
		(*brush).DrawWTexture();
		i++;
	}

	for(auto& mesh : g_static_meshes)
	{
		mesh.Draw();
	}

	for(auto& p_ent : g_entities)
	{
		p_ent->Draw();
	}
	//g_entities[0].Draw();

	Renderer::CheckGLError("AfterDraw");
	Renderer::SwitchProgram(CONSTANT_PROGRAM);

	glUniformMatrix4fv(g_render_state.current_program->uniform(MVP_UNIFORM), 1, GL_FALSE, view_proj_mat);

	g_builder_brush->Draw();

	g_selection_frame->Draw();

}
